//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  The basic module for MapWindow version 6.0
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either expressed or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is from MapWindow.dll version 6.0
//
// The Initial Developer of this Original Code is Ted Dunsford. Created 3/8/2008 7:19:53 AM
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************

using System;
using System.ComponentModel;
using System.Collections.Generic;   
using System.Drawing;
using System.IO;
using MapWindow.Components;
using MapWindow.Data;
using MapWindow.Drawing;
using MapWindow.Geometries;
using MapWindow.Main;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;


namespace MapWindow.DirectX
{


    /// <summary>
    /// Texture
    /// </summary>
    public class RasterLayerDX: RasterLayer, IRasterLayerDX
    {
        #region Events

        /// <summary>
        /// Occurs when the texture has finished loading
        /// </summary>
        public event EventHandler TextureLoaded;

        #endregion

        #region Private Variables

        private TextureStyles _style;
        private float _opacity;
        private string _worldFile;
        private TextureComponent _texture;
        private IndexCollection _indices;
        private VertexCollection _vertices;
        private DetailLevels _detailLevel;
        private BackgroundWorker backgroundWorker1;
        private System.Windows.Forms.Timer timer1;
        private int _loadingStage = 1;

        private EnvelopeDX _loadingRect;
        private bool _isLoaded;
        private double _elevationOffset;
        //private double _elevationScale = 2.74484E-07; // feet to decimal degrees
        private VertexModes _vertexMode;
        private readonly Image _sourceTexture;

        private class DeviceFile
        {
            public readonly string Filename;
            public readonly Device Device;
            public readonly Image SourceImage;

            public DeviceFile(Device device3D, string textureFilename)
            {
                Device = device3D;
                Filename = textureFilename;
            }

            public DeviceFile(Device device3D, Image sourceImage)
            {
                Device = device3D;
                SourceImage = sourceImage;
            }

           
        }


        #endregion

        #region Constructors


        /// <summary>
        /// Creates a texture with the specified file.  A Load method will open the existing texture.  A Save method will overwrite it.
        /// </summary>
        /// <param name="filename">The filename to associate with this texture.</param>
        public RasterLayerDX(string filename)
            :base(filename)
        {
            DataSet = DataManager.DefaultDataManager.OpenRaster(filename, true, null);
            Configure();


        }

        /// <summary>
        /// Creates a texture with the specified file.  A Load method will open the existing texture.  A Save method will overwrite it.
        /// </summary>
        /// <param name="filename">The filename to associate with this texture.</param>
        /// <param name="progressHandler">An IProgressHandler for receiving status messages</param>
        public RasterLayerDX(string filename, IProgressHandler progressHandler)
            :base(filename, progressHandler)
        {

            Configure();
        }

        /// <summary>
        /// Creates a texture with the specified file.  A Load method will open the existing texture.  A Save method will overwrite it.
        /// </summary>
        /// <param name="filename">The filename to associate with this texture.</param>
        /// <param name="container">A container to add this new layer to</param>
        /// <param name="progressHandler">An IProgressHandler for receiving status messages</param>
        public RasterLayerDX(string filename, IProgressHandler progressHandler, ICollection<ILayer> container)
            :base(filename, progressHandler)
        {
            Configure();
            container.Add(this);
        }

        /// <summary>
        /// Creates a layer from the existing raster
        /// </summary>
        /// <param name="raster"></param>
        public RasterLayerDX(IRaster raster):base(raster)
        {
            DataSet = raster;
            base.ProgressHandler = raster.ProgressHandler;
            Configure();
        }

        /// <summary>
        /// Creates an instance of this raster layer based on an existing raster
        /// </summary>
        /// <param name="raster">A valid implementation of IRaster</param>
        /// <param name="container">The container for this layer.</param>
        public RasterLayerDX(IRaster raster, ICollection<ILayer> container)
            : base(raster, DataManager.DefaultDataManager.ProgressHandler)
        {
            DataSet = raster;
            base.ProgressHandler = raster.ProgressHandler;
            Configure();
            container.Add(this);
        }

        /// <summary>
        /// This creates a new instance of a RasterLayerDX, but uses the specified
        /// image in order to construct a dynamic texture instead of loading it
        /// from the file.  This will cause the layer to ignore any bmp files
        /// with the same name as this raster.
        /// </summary>
        /// <param name="raster">The raster data that defines this layer</param>
        /// <param name="texture">A bitmap texture to use instead of symbology</param>
        /// <param name="container">The layer list that contains this raster</param>
        public RasterLayerDX(IRaster raster, Image texture, ICollection<ILayer> container)
            : base(raster, DataManager.DefaultDataManager.ProgressHandler)
        {
            DataSet = raster;
            base.ProgressHandler = raster.ProgressHandler;
            _sourceTexture = texture;
            Configure();
            container.Add(this);
        }

        /// <summary>
        /// This creates a new instance of a RasterLayerDX, but uses the specified
        /// image in order to construct a dynamic texture instead of loading it
        /// from the file.  This will cause the layer to ignore any bmp files
        /// with the same name as this raster.
        /// </summary>
        /// <param name="raster">The raster data that defines this layer</param>
        /// <param name="texture">A bitmap texture to use instead of symbology</param>
        /// <param name="container">The layer list that contains this raster</param>
        /// <param name="progressHandler">The progress handler for progress messages</param>
        public RasterLayerDX(IRaster raster, Image texture, ICollection<ILayer> container, IProgressHandler progressHandler)
            : base(raster, DataManager.DefaultDataManager.ProgressHandler)
        {
            DataSet = raster;
            base.ProgressHandler = progressHandler;
            _sourceTexture = texture;
            Configure();
            container.Add(this);
            
        }
        
      
    

        private void Configure()
        {
            if (DataSet == null) return;
            LegendText = Path.GetFileNameWithoutExtension(DataSet.Filename);
            DataSet.Bounds.BoundsChanged += _bounds_BoundsChanged;
            InitializeBackgroundWorker();
        }

        void _bounds_BoundsChanged(object sender, EventArgs e)
        {
            base.Invalidate();
            _vertices.Invalidate();
        }

        private void InitializeBackgroundWorker()
        {
            backgroundWorker1 = new BackgroundWorker();
            backgroundWorker1.DoWork += backgroundWorker1_DoWork;
            backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;

            timer1 = new System.Windows.Forms.Timer();
            timer1.Interval = 1000;
            timer1.Tick += timer1_Tick;
            //if (ProgressHandler != null)
            //{
            //    progressMeter1 = new ProgressMeter(ProgressHandler, "Creating Mesh", 100);
            //}
        }

        
    
        #endregion

        #region Methods

        /// <summary>
        /// Disposes the unmanaged memory objects
        /// </summary>
        public void Dispose()
        {
            _indices.Dispose();
            _vertices.Dispose();
            _sourceTexture.Dispose();
        }


        /// <summary>
        /// This is the internal drawing method that follows the standard checks.  Drawing is evoked
        /// through the Draw3D command, but internally implemented in the DrawPart method.  After
        /// the initial checks, the Draw3D method will cycle the part integer from 0 to NumParts -1.
        /// </summary>
        /// <param name="args">A DrawArgsDX class with the device and camera.</param>
        protected virtual void OnDraw(DrawArgsDX args)
        {
            if (_loadingRect == null)
            {
                _loadingRect = new EnvelopeDX(Color.LightGreen, Envelope);
            }
            _loadingRect.Draw3D(args);
            

            // If we don't have an image loaded yet, just draw a rectangle.
            if (_isLoaded == false)
            {
                return;
            }

            _indices.BeforeDrawing(args.Device);
            _texture.BeforeDrawing(args.Device);
            _vertices.BeforeDrawing(args.Device);

            args.Device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, _vertices.Count, 0, _indices.Count / 3);

            _indices.AfterDrawing(args.Device);
            _texture.AfterDrawing(args.Device);
            _vertices.AfterDrawing(args.Device);


        }

  

        
       

        /// <summary>
        /// Clears directX memory during dispose
        /// </summary>
        /// <param name="disposing">boolean disposing</param>
        protected override void Dispose(bool disposing)
        {
            if(_texture != null)_texture.Dispose();
            if(_indices != null)_indices.Dispose();
            if(_vertices != null)_vertices.Dispose();
           
            base.Dispose(disposing);
        }

        /// <summary>
        /// Closes the memory objects on this layer
        /// </summary>
        public virtual void Close()
        {
            if (_texture != null) _texture.Dispose();
            if (_indices != null) _indices.Dispose();
            if (_vertices != null) _vertices.Dispose();
            _isLoaded = false;
            IsInitialized = false;
            
        }
        

       
        /// <summary>
        /// Opens the texture layer, but doesn't automatically draw it.  Rather it simply creates the texture so that it can be used
        /// by another framework.
        /// </summary>
        /// <param name="device">The Microsoft.DirectX.Direct3D.Device to draw to</param>
        /// <param name="camera"></param>
        public virtual void Open(Device device, Camera camera)
        {
            Open(device, camera, ProgressHandler);
        }

        /// <summary>
        /// This causes this textureLayer to open a data file and load the texture, but does not render the texture.
        /// This should be called before draping this texture on a different frame.
        /// </summary>
        /// <param name="device">A Microsoft.DirectX.Direct3D.Device</param>
        /// <param name="camera">A Camera</param>
        /// <param name="progressHandler">progress handler</param>
        public virtual void Open(Device device, Camera camera, IProgressHandler progressHandler)
        {
            ProgressHandler = progressHandler;
         
            LoadHeader();
            Initialize(new DrawArgsDX(device, camera));
        }

        /// <summary>
        /// Not sure if I need this for rasters
        /// </summary>
        public void LoadHeader()
        {
          
            
            //string ext = System.IO.Path.GetExtension(_filename);
            //if (ext == ".bmp" || ext == ".jpg" || ext == ".gif")
            //{
            //    System.IO.FileStream fs = null;
            //    try
            //    {
            //        fs = new FileStream(Filename, FileMode.Open, FileAccess.Read, FileShare.Read);
            //        using (System.Drawing.Image image = System.Drawing.Image.FromStream(fs))
            //        {
            //            _fileSize = fs.Length;
            //            _numRows = image.Height;
            //            _numColumns = image.Width;
            //        }
            //        fs.Close();
            //        fs = null;
            //    }
            //    catch
            //    {
            //        // this is just a backup and means that the file is not a DotNet image type and I have no idea how many rows or columns it has
            //        _numRows = 100;
            //        _numColumns = 100;
            //    }
            //    finally
            //    {
            //        if (fs != null)
            //        {
            //            try
            //            {
            //                fs.Close();
            //            }
            //            finally
            //            {
            //                fs.Dispose();
            //            }
            //        }
            //    }

                
            //}

            //if (_style == TextureStyles.Georeferenced)
            //{
            //    if (System.IO.File.Exists(_worldFile))
            //    {
            //        _bounds = new RasterBounds(_numRows, _numColumns, _worldFile);
            //    }
            //}
            
        }

        /// <summary>
        /// Unlike shapefiles, textures are very easilly moved, so this method allows
        /// </summary>
        /// <param name="extents">The extents to move this layer to</param>
        public virtual void MoveTo(IEnvelope extents)
        {
            int w = 256;
            int h = 256;
            if (_texture != null)
            {
                Image size = _texture.ToImage();
                w = size.Width;
                h = size.Height;
            }
            double[] aff = new double[6];
            aff[1] = extents.Width/w; // cell width
            aff[2] = 0.0; // skew X
            aff[4] = 0.0; // skew y
            aff[5] = -(extents.Height / h); // cell height
            aff[0] = extents.Minimum.X; // left 
            aff[3] = extents.Maximum.Y;// top
            DataSet.Bounds = new RasterBounds(h, w, aff);
          
            
        }


        /// <summary>
        /// This is necessary if the frame is supposed to move.  It will force it to be regenerated during a draw.
        /// </summary>
        public virtual void ResetMesh()
        {
            if (_vertices != null) _vertices.Invalidate();

        }


        /// <summary>
        /// this will force the texture to reload
        /// </summary>
        public virtual void ResetTexture()
        {
            if (_texture != null) _texture.Invalidate();
        }

       
        /// <summary>
        /// This layer doesn't actually support changing the image.  Instead, this simply updates the worldfile,
        /// but only if the style is georeferenced.
        /// </summary>
        public virtual void Save()
        {
            if (_style == TextureStyles.Georeferenced)
            {
                DataSet.Bounds.SaveAs(_worldFile);
            }
        }

       
        #endregion

        #region Properties

      

        /// <summary>
        /// Gets or sets the level of detail for the terrain map
        /// </summary>
        [Category("General"), Description("Gets or sets the level of detail for the terrain map.")]
        public DetailLevels DetailLevel
        {
            get { return _detailLevel; }
            set 
            { 
                _detailLevel = value;
                if (_vertices != null)
                {
                    _vertices.Invalidate();
                    //IsInitialized = false;
                    base.Invalidate();
                }
            }
        }

       

      

        /// <summary>
        /// Gets or sets a double valued offset in order to adjust the displayed elevation of the layer.
        /// </summary>
        [Category("Symbology"), Description("Gets or sets a double valued offset in order to adjust the displayed elevation of the layer.")]
        public double ElevationOffset
        {
            get { return _elevationOffset; }
            set { _elevationOffset = value; }
        }

        ///// <summary>
        ///// OBSOLETE - being removed.  Use ElevationFactor instead.
        ///// </summary>
        //[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        //public double ElevationScale
        //{
        //    get { return _elevationScale; }
        //    set 
        //    { 
        //        _elevationScale = value;
        //        if (_vertices != null)
        //        {
        //            _vertices.Invalidate();
        //        }
        //    }
        //}

        /// <summary>
        /// Obtains an envelope that contains this image
        /// </summary>
        /// <returns>An IEnvelope</returns>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public override IEnvelope Envelope
        {
            get
            {
                if (DataSet == null) return null;
                if (DataSet.Bounds == null) return null;
                return DataSet.Bounds.Envelope;
            }
           
        }

      



        /// <summary>
        /// Gets whether this tile has been loaded
        /// </summary>
        public bool IsLoaded
        {
            get { return _isLoaded; }
            protected set { _isLoaded = value; }
        }

        


        /// <summary>
        /// Gets or sets a float from 0 to 1, where 0 is fully transparent and 1 is fully opaque
        /// </summary>
        [Category("Appearance"), Description("A float from 0 to 1, where 0 is fully transparent and 1 is fully opaque")]
        public virtual float Opacity
        {
            get { return _opacity; }
            set { _opacity = value; }
        }

       


        /// <summary>
        /// Describes the basic way this texture image should be applied to a given 3D structure.
        /// </summary>
        [Category("Behavior"), Description("Describes the basic way this texture image should be applied to a given 3D structure.")]
        public virtual TextureStyles Style
        {
            get { return _style; }
            set { _style = value; }
        }

        /// <summary>
        /// Gets or sets the ITexture
        /// </summary>
        public virtual Texture Texture
        {
            get { return _texture.TextureDX; }
            set 
            {
                if (_texture == null) _texture = new TextureComponent();
                _texture.TextureDX = value;
            }
        }

        /// <summary>
        /// Gets or sets the VertexMode for how the elevation vertices are mapped to the grid values
        /// </summary>
        [Category("General"), Description("NearestNeighbor positions vertices at centers, Bilinear interpolation positions calculated vertices on edges of grid cells.")]
        public VertexModes VertexMode
        {
            get { return _vertexMode; }
            set { _vertexMode = value; }
        }

        /// <summary>
        /// Gets or sets a world file.  If the worldfile obides by conventional naming, this doesn't 
        /// need to be set.
        /// </summary>
        [Category("Data"), Description("Gets or sets a world file.  This will look for the conventional naming scheme when the filename is set.")]
        public string WorldFile
        {
            get { return _worldFile; }
            set { _worldFile = value; }
        }

    
        #endregion


        #region Private Functions


        private void AddVertices(int numRows, int numColumns)
        {
            
            RasterSequencer rSeq = new RasterSequencer(DataSet, numRows, numColumns);
            rSeq.VertexMode = _vertexMode;
            PositionTexturedVC verts = _vertices as PositionTexturedVC;
            while(rSeq.MoveNext())
            {
                Coordinate c = rSeq.Current;
                if (c.Z < 1E-20)
                {
                    c.Z = 0;
                }
                c.Z = c.Z * ElevationFactor + _elevationOffset;

                if (verts != null) verts.AddCoordinate(rSeq.Current, rSeq.TU, rSeq.TV);
            }
        }


        /// <summary>
        /// Copies properties
        /// </summary>
        /// <param name="editCopy"></param>
        protected override void OnCopyProperties(object editCopy)
        {
            // Copy layer properties
            base.OnCopyProperties(editCopy);

            RasterLayerDX copy = editCopy as RasterLayerDX;
            if (copy == null) return;
            _texture.Invalidate();
            _vertices.Invalidate();
            Invalidate(); // we changed characteristics so we will need to re-run the initialize
        }

        /// <summary>
        /// Initializes the directX memory structures so that they can be quickly rendered from memory.
        /// </summary>
        /// <param name="args">A DrawArgsDX that has all the necessary information to control drawing.</param>
        public void Initialize(DrawArgsDX args)
        {
            OnInitialize(args);
        }

        /// <summary>
        /// Sets up the texture if it has never been drawn before
        /// </summary>
        protected virtual void OnInitialize(DrawArgsDX args)
        {
  
            if (_vertices == null || _vertices.IsInitialized == false)
            {
                _vertices = new PositionTexturedVC();
                _vertices.DrawBox = args.Camera.DrawBox;
                int nR = 1;
                int nC = 1;
                // Top Left
                if (_detailLevel == DetailLevels.Flat)
                {
                    _indices = new IndexCollection(1);
                    _vertices.AddCoordinate(DataSet.Bounds.TopLeft());
                    _vertices.AddCoordinate(DataSet.Bounds.TopRight());
                    _vertices.AddCoordinate(DataSet.Bounds.BottomLeft());
                    _vertices.AddCoordinate(DataSet.Bounds.BottomRight());
                }
                else
                {
                    if(_detailLevel == DetailLevels.High)
                    {
                        nC = 1000;
                        nR = 1000;
                    }
                    else if (_detailLevel == DetailLevels.Medium)
                    {
                        nC = 500;
                        nR = 500;
                    }
                    else if (_detailLevel == DetailLevels.Low)
                    {
                        nC = 100;
                        nR = 100;
                    }
                    if (DataSet.NumRows < nR) nR = DataSet.NumRows;
                    if (DataSet.NumColumns < nC) nC = DataSet.NumColumns;
                    _indices = new IndexCollection(nR, nC, _vertexMode);

                    // The vertices and triangles are generated in a background thread.
                    //RowColArgument rc = new RowColArgument(nR, nC);
                    //backgroundWorker1.RunWorkerAsync(rc);

                    AddVertices(nR, nC);
                    //if (progressMeter1 != null)
                    //{
                    //    timer1.Interval = 1000;
                    //    timer1.Start();
                    //}
                    //AddVertices(nR, nC);

                    //Determine the step of the progress bar depending on number of vertices
                    //int percent = 0;
                    //int step = 1;
                    //if (nR * nC <= 500000)
                    //{
                    //    step = 2;
                    //}
                    //if (nR * nC <= 250000)
                    //{
                    //    step = 4;
                    //}

                    //string progressMessage = "Creating Mesh.. ";
                    //if (_loadingStage > 1)
                    //{
                    //    progressMessage = "Loading.. ";
                    //}

                    //while (backgroundWorker1.IsBusy)
                    //{
                    //    if (ProgressHandler != null)
                    //    {
                    //        System.Threading.Thread.Sleep(200);
                    //        //progressMeter1.CurrentPercent++;
                            
                    //        ProgressHandler.Progress("", percent, progressMessage + " " + percent + "%");
                    //        percent += step;
                    //        if (percent == 100)
                    //        {
                    //            percent = 0;
                    //        }
                    //        System.Windows.Forms.Application.DoEvents();                          
                    //    }
                    //}
                }
            }
            if ((_texture == null || _texture.IsInitialized == false) && backgroundWorker1.IsBusy == false)
            {
                DeviceFile df;
                if (_sourceTexture == null)
                {
                    // If they pass in a source texture bitmap, don't create any new file.
                    string file = System.IO.Path.ChangeExtension(DataSet.Filename, ".bmp");
                    _texture = new TextureComponent(file);
                    if (System.IO.File.Exists(file) == false)
                    {
                        Symbolizer.CreateBitmap();

                    }
                    df = new DeviceFile(args.Device, file);
                }
                else
                {
                    df = new DeviceFile(args.Device, _sourceTexture);
                }
                
                // we need to generate a raster bitmap.
                backgroundWorker1.RunWorkerAsync(df);
                
            }
                //_texture.TextureDX.SetLevelOfDetail(0);
            if (Inititialized != null)
            {
                Inititialized(this, args);
            }
           
        }

        // This event handler is where the actual,
        // potentially time-consuming work is done.
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {

            if (e.Argument is DeviceFile)
            {
                // Execute the 'LoadTexture' function in a separate thread
                DeviceFile df = e.Argument as DeviceFile;
                if (df.SourceImage == null)
                {
                    e.Result = LoadTexture(df.Device, df.Filename);
                }
                else
                {
                    e.Result = LoadTexture(df.Device, df.SourceImage);
                }
            }
            else if (e.Argument is RowColArgument)
            {
                // Execute the 'AddVertices' function in a separate thread
                RowColArgument arg = e.Argument as RowColArgument;
                {
                    AddVertices(arg.NumberRows, arg.NumberColumns);
                }
            }
        }

        // This event handler deals with the results of the
        // background operation.
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Result is Texture)
            {

                Texture = e.Result as Texture;
                //if (_progressMeter != null) _progressMeter.Reset();
                _isLoaded = true;
                OnItemChanged();
                OnTextureLoaded(this, new EventArgs());
            }
            else
            {
                if (ProgressHandler != null)
                {
                    ProgressHandler.Progress("", 0, "");
                }
                _loadingStage++;
            }
            if (timer1 != null)
            {
                timer1.Stop();
            }
        }

        // This event updates the progress bar and status label
        private void timer1_Tick(object sender, EventArgs e)
        {
            //TimeSpan ts = DateTime.Now.Subtract(startDate);
            //string sTime = "  ..." + ts.Minutes.ToString("00") +
            //   ":" + ts.Seconds.ToString("00") +
            //   ":" + ts.Milliseconds.ToString("000");
            //toolStripStatusLabelTime.Text = sTime;

            if (ProgressHandler != null)
            {
                ProgressHandler.Progress("", 50, "Creating Mesh..");
            }

            //if (progressMeter1 != null)
            //{
            //    if (progressMeter1.CurrentPercent >= 99)
            //    {
            //        progressMeter1.CurrentPercent = 0;
            //    }
            //    progressMeter1.CurrentPercent++;
            //}
        }


       

        // actually loads the texture
        static Texture LoadTexture(Device device, string filename)
        {          
            try
            {
                return TextureLoader.FromFile(device, filename);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
            }
            return null;
        }

        static Texture LoadTexture(Device device, Image image)
        {
            try
            {
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                image.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
                ms.Position = 0;
                return TextureLoader.FromStream(device, ms);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
            }
            return null;
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Raises the TextureLoaded event for this layer
        /// </summary>
        /// <param name="sender">Generally the raster layer</param>
        /// <param name="e">A System.EventArgs</param>
        protected virtual void OnTextureLoaded(object sender, EventArgs e)
        {
            if (TextureLoaded != null)
            {
                TextureLoaded(sender, e);
            }
        }

        /// <summary>
        /// Occurs immediately before drawing
        /// </summary>
        /// <returns>boolean</returns>
        protected virtual bool OnBeforeDrawing(DrawVerifyArgsDX e)
        {
            if (_vertices != null)
            {
                if (e.Camera.DrawBox != _vertices.DrawBox)
                {
                    _vertices.Invalidate();
                }
            }
            if (BeforeDrawing != null) BeforeDrawing(this, e);
            return e.Cancel;
        }

        #endregion







        #region IRasterLayer Members

       

        

      

        #endregion

     


        // The following code region represents methods that are common between Point, Line, and Polygon layers.
        #region Common Rendering Content

        #region Events

        /// <summary>
        /// Occurs immediately before drawing, allowing it to be cancelled
        /// </summary>
        public event EventHandler<DrawVerifyArgsDX> BeforeDrawing;

        /// <summary>
        /// Occurs after drawing, indicating if an exception or cancellation occured
        /// </summary>
        public event EventHandler<DrawCompletedArgsDX> DrawingCompleted;

        /// <summary>
        /// Occurs when the data has been loaded into the model after the first drawing method
        /// </summary>
        public event EventHandler<DrawArgsDX> Inititialized;


        #endregion

        #region Private Variables

        private int _numParts = 1;
        private int _numStages = 1;
        private DrawBox _drawBox;
        private float _previousScale;

        #endregion

        #region Methods




        /// <summary>
        /// Causes the layer or object to draw itself to the map.  This may initiate
        /// one-time loading of buffers, but should not do heavy calculations every
        /// cycle.
        /// </summary>
        /// <param name="args">A Drawing Args structure.  This is used so that this method
        /// can also be called asynchronously</param>
        public virtual void Draw3D(DrawArgsDX args)
        {
            // try
            // {
            if (IsVisible == false)
            {
                OnDrawingCompleted(new DrawCompletedArgsDX(args, false, null));
                return;
            }
            if (OnBeforeDrawing(new DrawVerifyArgsDX(args, false)))
            {
                OnDrawingCompleted(new DrawCompletedArgsDX(args, true, null));
                return;
            }

            OnDrawSetup(new DrawSetupArgs(args.Camera));
            args.Camera.Position(args.Device);

            if (IsInitialized == false)
            {
                Initialize(args);
            }




            for (int stage = 0; stage < NumStages; stage++)
            {
                for (int part = 0; part < NumParts; part++)
                {
                    DrawArgsDX innerArgs = new DrawArgsDX(args.Device, args.Camera, part, stage);
                    OnDraw(innerArgs);
                }
            }
            OnDrawingCompleted(new DrawCompletedArgsDX(args, false, null));
            //}
            //catch (Exception ex)
            //{
            //    OnDrawingCompleted(this, new DrawCompletedArgsDX(args, false, ex));
            //}


        }




        /// <summary>
        /// Determines the geographic distance to use for something that is one pixel wide on the screen
        /// </summary>
        /// <param name="args">A DrawArgsDX with a Microsoft.DirectX.Device</param>
        /// <returns>A float multiplier for the scale</returns>
        public float GetScale(DrawArgsDX args)
        {
            Viewport vp = args.Device.Viewport;
            Transforms t = args.Device.Transform;
            // Find the point along the view path of the camera that intersects with the 0 elevation of the z plane
            //Vector3 pt = Plane.IntersectLine(args.Camera.XYPlane, args.Camera.CameraPosition, args.Camera.TargetPosition);
            Vector3 pt = Vector3.Subtract(args.Camera.TargetPosition, args.Camera.CameraPosition);
            float z = Math.Abs(args.Camera.CameraPosition.Z);
            pt.X = pt.X * z / pt.Z;
            pt.Y = pt.Y * z / pt.Z;
            pt = Vector3.Add(args.Camera.CameraPosition, pt);
            pt.Z = 0;

            // Project that into screen space in order to determine where the 0-elevation plane is between the near plane an the far plane of the scene
            Vector3 depth = Vector3.Project(pt, vp, t.Projection, t.View, t.World);

            // Back project two pixels from the screen into world coordinates that are separated by a distance of 1 pixel
            Vector3 center = new Vector3((float)vp.Width / 2, (float)vp.Height / 2, depth.Z);
            Vector3 right = new Vector3(vp.Width / 2 + 1, (float)vp.Height / 2, depth.Z);
            Vector3 wCenter = Vector3.Unproject(center, vp, args.Device.Transform.Projection, args.Device.Transform.View, args.Device.Transform.World);
            Vector3 wRight = Vector3.Unproject(right, vp, args.Device.Transform.Projection, args.Device.Transform.View, args.Device.Transform.World);

            // The distance between the two locations in world coordinates gives a scale value.
            Vector3 diff = Vector3.Subtract(wRight, wCenter);

            return diff.Length();
        }



        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the draw box for this PointLayer
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public DrawBox DrawBox
        {
            get { return _drawBox; }
            set { _drawBox = value; }
        }

        /// <summary>
        /// Gets the number of parts in this drawing structure
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public virtual int NumParts
        {
            get { return _numParts; }
            protected set { _numParts = value; }
        }

        /// <summary>
        /// Gets the number of stages that will be drawn.  Each stage cycles through every part.
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public virtual int NumStages
        {
            get { return _numStages; }
            protected set { _numStages = value; }
        }

        /// <summary>
        /// This property is computed during re-scaling.  If the ScaleMode is set to Symbolic, this
        /// will represent the scale that was used when initializing the symbols.
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public virtual float PreviousScale
        {
            get
            {
                return _previousScale;
            }
            set
            {
                _previousScale = value;
            }
        }



        #endregion

        #region Protected Methods


        


        /// <summary>
        /// Fires an event when we are done with the drawing code, whether the drawing was successful, cancelled, or threw an exception.
        /// </summary>
        /// <param name="e">A DrawCompletedArgsDX parameter containing information about the drawing</param>
        protected virtual void OnDrawingCompleted(DrawCompletedArgsDX e)
        {
            if (DrawingCompleted == null) return;
            DrawingCompleted(this, e);
        }

        /// <summary>
        /// Ensures that if the mode is symbolic, the initialization code will occur every time.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnDrawSetup(DrawSetupArgs e)
        {
            if (_drawBox == null || e.Camera.DrawBox.Equals(_drawBox) == false)
            {
                IsInitialized = false;
                _drawBox = e.Camera.DrawBox;
            }
        }

        /// <summary>
        /// Invalidates this layer
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void OnInvalidate(object sender, EventArgs e)
        {
            _texture.Invalidate();
            _vertices.Invalidate();
            base.OnInvalidate(sender, e);
        }


      



        #endregion

        #endregion

    }

}
